{-
 Copyright 2022-23, Juspay India Pvt Ltd

 This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License

 as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program

 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY

 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of

 the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE OverloadedLabels #-}

module PrepareDataForLoadTest
  ( prepareDataForLoadTest,
    defaultPrivateKey,
    cleanupData,
    runK6Script,
  )
where

import qualified Beckn.Types.Core.Taxi.API.Search as API
import qualified Beckn.Types.Core.Taxi.Search as API
import Control.Lens ((.~))
import qualified Data.Aeson as J
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import qualified Data.Time.Clock.POSIX as Time
import qualified EulerHS.Language as L
import EulerHS.Prelude hiding ((.~))
import Kernel.Types.Base64
import qualified Kernel.Types.Beckn.Context as API
import qualified Kernel.Types.Beckn.ReqTypes as API
import Kernel.Utils.Example (Example (example))
import qualified Kernel.Utils.SignatureAuth as S
import System.Directory (removeFile)

data RequestForLoadTest = RequestForLoadTest
  { rawRequest :: !Text,
    signature :: !Text
  }
  deriving (Show, ToJSON, FromJSON, Generic)

-- This keys was generated by generateKeyPair function and used for local testing:
-- privateKey = "Lw9M+SHLY+yyTmqPVlbKxgvktZRfuIT8nHyE89Jmf+o="
-- publicKey = "1tRC4LHVKWeGikY5C8T1FYI9t3oacpa3IFvj8e7aGws="

defaultPrivateKey :: ByteString
defaultPrivateKey = "Lw9M+SHLY+yyTmqPVlbKxgvktZRfuIT8nHyE89Jmf+o="

prepareDataForLoadTest :: ByteString -> Int -> Text -> L.Flow ()
prepareDataForLoadTest privateKey nmbOfReq filePath = do
  reqs <- replicateM nmbOfReq $ do
    request <- generateSearchRequest
    now <- L.runIO Time.getPOSIXTime
    pure $ do
      let body = LBS.toStrict $ J.encode request
      let bodyHash = S.becknSignatureHash body
      let headers = [("(created)", ""), ("(expires)", ""), ("digest", "")]
      let signatureParams = S.mkSignatureParams "JUSPAY.MOBILITY.APP.UAT.1" "juspay-mobility-bap-1-key" now 600 S.Ed25519
      signature <- S.sign (Base64 $ Base64.decodeLenient privateKey) signatureParams bodyHash headers
      pure $ RequestForLoadTest (decodeUtf8 body) (decodeUtf8 $ S.encode $ S.SignaturePayload signature signatureParams)
  L.runIO . writeFile (T.unpack filePath) . decodeUtf8 . J.encode . catMaybes $ reqs

cleanupData :: Text -> L.Flow ()
cleanupData path = do
  L.logInfo @Text "GenerateRequestsForLoadTest" "Cleaning up..."
  L.runIO . removeFile . T.unpack $ path

runK6Script :: Text -> Text -> Int -> L.Flow String
runK6Script url filePath nmbOfReq = do
  L.logInfo @Text "GenerateRequestsForLoadTest" "Start K6 script..."
  L.runSysCmd $
    "k6 run -e LOAD_TEST_URL="
      <> T.unpack url
      <> " -e FILE_PATH="
      <> T.unpack filePath
      <> " ./dev/load-test/script.js"
      <> " -e N_REQ="
      <> show nmbOfReq

generateSearchRequest :: L.Flow API.SearchReq
generateSearchRequest = do
  txnId <- L.generateGUID
  let context = example @API.Context & #message_id .~ txnId
  let intent =
        API.SearchMessage
          { intent =
              API.Intent
                { fulfillment =
                    API.FulfillmentInfo
                      { start =
                          API.StartInfo
                            { location =
                                API.Location
                                  { gps = API.Gps {lat = 20.5937, lon = 78.9629},
                                    address = Nothing
                                  }
                            },
                        end =
                          Just $
                            API.StopInfo
                              { location =
                                  API.Location
                                    { gps = API.Gps {lat = 20.5937, lon = 78.9629},
                                      address = Nothing
                                    }
                              },
                        tags = Nothing,
                        customer = Nothing
                        -- API.Tags
                        --   { --customer_language = Nothing
                        --     code = Nothing,
                        --     name = Nothing,
                        --     list_1_code = Nothing,
                        --     list_1_name = Nothing,
                        --     list_1_value = Nothing,
                        --     list_2_code = Nothing,
                        --     list_2_name = Nothing,
                        --     list_2_value = Nothing
                        --   }
                      }
                }
          }
  pure $ API.BecknReq context intent
